package com.secure.support.resttemplate;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpRequest;
import org.springframework.http.StreamingHttpOutputMessage;
import org.springframework.http.client.*;
import org.springframework.util.StreamUtils;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;

/**
 * 自定义HttpRequest
 */
public class RestClientHttpRequest extends AbstractClientHttpRequest {

    private final HttpRequest httpRequest;
    private final URI uri;

    private ByteArrayOutputStream bufferedOutput = new ByteArrayOutputStream(1024);

    protected RestClientHttpRequest(HttpRequest httpRequest, URI uri) {
        this.httpRequest = httpRequest;
        this.uri = uri;
    }

    public HttpMethod getMethod() {
        return httpRequest.getMethod();
    }

    public String getMethodValue() {
        return httpRequest.getMethodValue();
    }

    public URI getURI() {
        return uri;
    }

    protected OutputStream getBodyInternal(HttpHeaders headers) throws IOException {
        return this.bufferedOutput;
    }

    protected ClientHttpResponse executeInternal(HttpHeaders headers) throws IOException {
        byte[] bytes = this.bufferedOutput.toByteArray();
        if (headers.getContentLength() < 0L) {
            headers.setContentLength((long) bytes.length);
        }

        ClientHttpResponse result = this.executeInternal(headers, bytes);
        this.bufferedOutput = new ByteArrayOutputStream(0);
        return result;
    }

    protected final ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException {
        RestRequestExecution requestExecution = new RestRequestExecution();
        return requestExecution.execute(this, bufferedOutput);
    }

    private class RestRequestExecution implements ClientHttpRequestExecution {
        public ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException {
            ClientHttpRequest delegate = new SimpleClientHttpRequestFactory().createRequest(RestClientHttpRequest.this.getURI(), RestClientHttpRequest.this.getMethod());
            request.getHeaders().forEach((key, value) -> {
                delegate.getHeaders().addAll(key, value);
            });
            if (body.length > 0) {
                if (delegate instanceof StreamingHttpOutputMessage) {
                    StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) delegate;
                    streamingOutputMessage.setBody((outputStream) -> {
                        StreamUtils.copy(body, outputStream);
                    });
                } else {
                    StreamUtils.copy(body, delegate.getBody());
                }
            }

            return delegate.execute();
        }
    }
}
